<?php
class File {
	
	protected 
		$_top,
		$_parent,
		$allow_change_extension = true,
		$extention,
		$name,
		$dir_path,
		$full_path,
		$delete_flag = false;
	
	public function __construct(&$top,&$parent,$file_name)
	{
		$this->_top =& $top;
		$this->_parent =& $parent;
		
		if ($file_name=="") throw $this->_top->Utils->PushError("File path can not be empty","Необходимо указывать имя файла");
		if (!file_exists($file_name)) throw $this->_top->Utils->PushError("File does not exsits", "По указанному пути файл найден");

		
		$file_info = pathinfo($file_name);
		
		$this->full_path = $file_name;
		$this->extention = $file_info['extension'];
		$this->dir_path = $this->_top->Utils->fixDirPath(realpath($file_info['dirname']));
		$this->name = $file_info['basename'];
		
	}
	
	/**
	 * Enter description here...
	 *
	 * @param String $new_dir_path
	 * @param String $check_rewriting
	 */
	public function Move($new_dir_path,$check_rewriting = false)
	{
		if (!is_dir($new_dir_path))  throw $this->_top->Utils->PushError("Need to set only dir name of new file location", "В качестве аргумента необходимо указать имя директории");
		if (!file_exists($new_dir_path)) throw $this->_top->Utils->PushError("Trying to move file in unexsistable directory $new_dir_path","Попытка перемещения файла в несуществующую директорию");
		$this->_top->Utils->fixDirPath($new_dir_path);
		if (!is_writable($new_dir_path)) throw $this->_top->Utils->PushError("Denied to move file in $new_dir_path", "Попытка переноса файла в директорию запрещенную для записи");
		
		if (file_exists($new_dir_path.$this->name)) {
			if ($check_rewriting) {
				throw $this->_top->Utils->PushError("File with such name already exsists in $new_dir_path","Попытка перезаписи существующего файла");
			} else {
				unlink($new_dir_path.$this->name);
			}
			
		}
		
		if (!rename($this->full_path,$new_dir_path.$this->name)) {
			throw $this->_top->Utils->PushError("Falied to move file {$this->full_path} in $new_dir_path", "Системная ошибка при переносе файла");
		} else {
			$this->__construct($this->_top,$this->_parent,$new_dir_path.$this->name);
		}
		
	}
	
	
	public function __get($param)
	{
		if (isset($this->$param)) {
			return $this->$param;
		} else {
			return false;
		}
	}
	
	public function __set($param,$value)
	{
		switch ($param) {
			case "name":
				$new_name_info = pathinfo($value);
				if (!$this->allow_change_extension && strtolower($new_name_info['extension'])!=strtolower($this->extention)) 
					throw $this->_top->Utils->PushError("Forbidden to change image file extension", "Попытка изменения расширения файла изображения");
				
				if (!$this->_top->Utils->file_name_validator($value)) 
					throw $this->_top->Utils->PushError("Denied to use forbidden symbols in file name","Неразрешено использовать запрещенные символы в имени файла");
				
				if (file_exists($this->dir_path.$value)) unlink($this->dir_path.$value);
				if (!rename($this->full_path,$this->dir_path.$value)) 
					throw $this->_top->Utils->PushError("Falied to rename file {$this->name} in $value", "Системная ошибка при переименовании файла");
				$this->__construct($this->_top,$this->_parent,$this->dir_path.$value);
				break;
			
			default:
				return false;
				break;
		}
		
		return true;
	}
	
	
	public function Copy($to,$check_rewriting = false)
	{
		if (is_dir($to)) {
			$this->_top->Utils->fixDirPath($to);
			$to .= $this->name;
		}
		
		$to_info = pathinfo($to);
		if (!file_exists($to_info['dirname'])) throw $this->_top->Utils->PushError("Trying to copy file in unexsistable directory {$to_info['dirname']}","Попытка копирования файла в несуществующую директорию");
		
		if ($check_rewriting && file_exists($to)) throw $this->_top->Utils->PushError("File '{$to_info['basename']}' already exsists in '{$to_info['dirname']}'","Попытка перезаписи существующего файла");
		
		if (!copy($this->full_path,$to)) throw $this->_top->Utils->PushError("Unable to copy file","Попытка копирования файла не удалась");
		
		chmod($to,0777);
		
		return new File($this->_top,$this->_parent,$to);
	}
	
	
	
	public function delete()
	{
		$this->delete_flag = true;
	}
	
	public function Push() {
		echo file_get_contents($this->full_path);
	}
	
	public function __destruct()
	{
		if ($this->delete_flag)
			unlink($this->full_path);
	}
	
}




class Image extends File 
{
	const thumb_dir = ".thumb";
	
	/**
	 * Качество файла выводимого
	 *
	 * @var integer
	 */
	public $quality = 100;
	
	private 
		$valid_extensions = array("jpg","jpeg","gif","png"),
		$width,
		$height;
	
	public function __construct(&$top,&$parent,$file_name)
	{
		parent::__construct($top,$parent,$file_name);
		
		$this->allow_change_extension = false;
		
		if (!in_array(strtolower($this->extention),$this->valid_extensions)) {	
			throw $this->_top->Utils->PushError("Unsupportable image format", "Поддерживаются только файлы изображений с расширением ".implode(",",$this->valid_extensions));
		}
		
		list($this->width,$this->height) = getimagesize($file_name);
	}
	
	
	
	public function __get($param)
	{
		if (isset($this->$param)) {
			return $this->$param;
		} else {
			return parent::__get($param);
		}
	}
	
	public function __set($param,$value)
	{
		switch ($param) {
			
			case "width":
				if (!is_int($value)) {
					throw $this->_top->Utils->PushError("Width of image must be integer", "Ширина файла должна быть целым числом");
				}
				
				$newHeight = round($this->height*$value/$this->width);
				$this->Resize($value,$newHeight);
				break;
			
			case "height":
				if (!is_int($value)) {
					throw $this->_top->Utils->PushError("Height of image must be integer", "Высота файла должна быть целым числом");
				}
				
				$newWidth = round($this->width*$value/$this->height);
				$this->Resize($newWidth,$value);
				break;
				
			default:
				return parent::__set($param,$value);;
				break;
		}
		
		return true;
	}
	
	
	public function Resize($width,$height)
	{
		switch (strtolower($this->extention)) {
			case "gif":
				$imSrc = imagecreatefromgif($this->full_path);
				break;
			
			case "jpg":
				$imSrc = imagecreatefromjpeg($this->full_path);
				break;
				
			case "jpeg":
				$imSrc = imagecreatefromjpeg($this->full_path);
				break;

			case "png":
				$imSrc = imagecreatefrompng($this->full_path);
				break;
				
			default:
				throw $this->_top->Utils->PushError("Creating image before resizing: Unsupportable image format", "Неподдерживаемый формат изображения");
			break;
		}
		
		$imDst = imagecreatetruecolor($width,$height);
		
		imagecopyresampled($imDst,$imSrc,0,0,0,0,$width,$height,$this->width,$this->height);
		
		switch (strtolower($this->extention)) {
			case "gif":
				imagegif($imDst,$this->full_path);
				break;
			
			case "jpg":
				imagejpeg($imDst,$this->full_path,$this->quality);
				break;
			
			case "jpeg":
				imagejpeg($imDst,$this->full_path,$this->quality);
				break;
			
			case "png":
				imagepng($imDst,$this->full_path,$this->quality);
				break;
				
			default:
				throw $this->_top->Utils->PushError("Saving after image resizing: Unsupportable image format", "Неподдерживаемый формат изображения");
			break;
		}
		
		$this->__construct($this->_top,$this->_parent,$this->full_path);
		
	}
	
	public function Copy($to,$check_rewriting = false)
	{
		$new_im = parent::Copy($to,$check_rewriting);
		return new Image($this->_top,$this->_parent,$new_im->full_path);
	}
	
	final public function Thumb() {
		$thumb_dir = $this->_top->Utils->fixDirPath($this->dir_path).self::thumb_dir;
		if (!file_exists($thumb_dir) || !is_dir($thumb_dir)) mkdir($thumb_dir);
		$new_im = parent::Copy($thumb_dir);
		return new Thumb($this->_top,$this->_parent,$new_im->full_path);
	}
	
}

class Thumb extends Image 
{
	
	public function __construct(&$top,&$parent,$file_name)
	{
		parent::__construct($top,$parent,$file_name);
	}
	
	public function __set($param,$value) {
		switch (strtolower($param)) {
			case 'width':
				if (!is_int($value)) {
					throw $this->_top->Utils->PushError("Width of image must be integer", "Ширина файла должна быть целым числом");
				}
				
				$newHeight = round($this->height*$value/$this->width);
				$this->Resize($value,$newHeight);
				break;
			
			case 'height':
				if (!is_int($value)) {
					throw $this->_top->Utils->PushError("Height of image must be integer", "Высота файла должна быть целым числом");
				}
				
				$newWidth = round($this->width*$value/$this->height);
				$this->Resize($newWidth,$value);
				break;
			default:
				parent::__set($param,$value);
			break;
		}
		
	}
	
	public function Resize($width,$height) {
		$new_name = explode('.', $this->name);
		$ext = array_pop($new_name);
		$new_name = join('_', $new_name).'_'.$width.'_'.$height.$ext;
		$this->name = $new_name;
		parent::Resize($width,$height);
	}
	
}


class Downloader {
	
	protected  
		$_top,
		$_parent,
		$protocol = "http",
		$resource = "",
		$timeout = 10;
	
	public function __construct($top,$parent,$resource = "")
	{
		$this->_top = $top;
		$this->_parent = $parent;
		$this->resource = $resource;
	}
	
	public function __set($param,$value)
	{
		if ($param=="protocol")
			$this->protocol = $value;
		elseif ($param=="resource")
			$this->resource = $value;
	}
	
		
	public function Make($resource = "")
	{
		if ($resource!="") $this->resource = $resource;
		
		switch ($this->protocol) {
			case "http":
				if( !function_exists("curl_init") &&
			      !function_exists("curl_setopt") &&
			      !function_exists("curl_exec") &&
			      !function_exists("curl_close") ) throw $this->_top->Utils->PushError("Curl Library does not installed");
				
			    if ($this->resource=="") throw $this->_top->Utils->PushError("Need to set resource first","Необходимо указать ссылку на ресурс");
			    
			    $res_info = pathinfo($this->resource);

			    $uc = curl_init();
      			curl_setopt($uc,CURLOPT_URL,$this->resource);
      			//curl_setopt($uc,CURLOPT_FILE,$target_file);
				curl_setopt($uc,CURLOPT_RETURNTRANSFER,1);
      			curl_setopt($uc,CURLOPT_TIMEOUT,$this->timeout);
      			curl_setopt($uc,CURLOPT_FAILONERROR, 1);
      			$result = curl_exec($uc);
				curl_close($uc);
				
				if (gettype($result)=="boolean" && !$result) throw $this->_top->Utils->PushError("Error occured while trying to access resourse","При обращении к ресурсу сервер вернул ошибку");
				$target_file_name = $this->_top->Utils->MindTempFile(strtolower($res_info['extension']));
			    $target_file = fopen($target_file_name,"w");
      			fwrite($target_file,$result);
				fclose($target_file);
				
				return $target_file_name;
				
				break;
			
			default:
				throw $this->_top->Utils->PushError("Invalid protocol");
				break;
		}
		
	}
	
	
}

class ImageDownloader extends Downloader   
{
	
	
	function Make() {
		$file_name = parent::Make();
		return new Image($this->_top,$this,$file_name);
	}
}

?>